#include "myglwidget.h"
#include "ilightsource.h"
#include <cmath>
#include <QKeyEvent>
#include <QApplication>

#define CAMERA_POSITION_INCREMENT   0.2f
#define CAMERA_ANGLE_INCREMENT      0.5f

MyGlWidget::MyGlWidget(QWidget *parent) : QGLWidget(parent)
{
    setFocusPolicy(Qt::ClickFocus);
}

MyGlWidget::~MyGlWidget()
{
}

void MyGlWidget::initializeGL()
{
    int gl_light_ids[] = {GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3, GL_LIGHT4, GL_LIGHT5, GL_LIGHT6, GL_LIGHT7};
    std::vector<ILightSource*> lights = scene_->light_sources();
    CgColor bgcolor = scene_->background_color();

    glShadeModel(GL_SMOOTH);
    glClearColor(bgcolor.r(), bgcolor.g(), bgcolor.b(), 1.0f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
//    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient_light);
    glEnable(GL_COLOR_MATERIAL);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
//    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );            // Activates wireframe mode
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );            // Deactivates wireframe mode
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

    scene_->opengl_setup_ambient_light();
    for(size_t i = 0 ; i < lights.size() ; i++) {
        const ILightSource *light = lights[i];
        glEnable(gl_light_ids[i]);
        light->glSetup(gl_light_ids[i]);
    }
}

void MyGlWidget::paintGL()
{
    double matrix_transform[16];
    std::vector<ICgObject*> objects = scene_->objects();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    updateCamera();

    for(size_t i = 0 ; i < objects.size() ; i++) {
        ICgObject *obj = objects[i];

        obj->transformation().toOpenGLMatrix(matrix_transform);
        glPushMatrix();
        glMultMatrixd(matrix_transform);    

        obj->openGlRender();
        glPopMatrix();
    }
}

void MyGlWidget::resizeGL(GLsizei width, GLsizei height)
{
    const ProjectionPlane pplane = scene_->camera().projection_plane();
    double fov = atan2((pplane.top() - pplane.bottom()) / 2.0, pplane.distance_from_camera()) * 180.0 / M_PI;
    double ratio = (pplane.right() - pplane.left()) / (pplane.top() - pplane.bottom());

    fov *= 2.0;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
//    gluPerspective(45.0, ((GLfloat)width)/height, 0.1f, 100.0f);
    gluPerspective(fov, ratio, pplane.distance_from_camera(), 100.0f);
    glMatrixMode(GL_MODELVIEW);
}

void MyGlWidget::keyPressEvent(QKeyEvent *ev)
{
    const double delta = 0.5;
    if( ev->key() == Qt::Key_Escape )
        QApplication::exit();
    else {
        Camera camera = scene_->camera();
        Vector displacement(0.0, 0.0, 0.0);
        Point new_camera_origin;

        switch( ev->key() ) {
        case Qt::Key_A:
            displacement(0) = -delta;
            break;
        case Qt::Key_D:
            displacement(0) = delta;
            break;
        case Qt::Key_Up:
            displacement(1) = delta;
            break;
        case Qt::Key_Down:
            displacement(1) = -delta;
            break;
        case Qt::Key_W:
            displacement(2) = -delta;
            break;
        case Qt::Key_S:
            displacement(2) = delta;
            break;
        case Qt::Key_P:
            emit asking_raycast();
        default:
            QWidget::keyPressEvent(ev);
            break;
        }

        new_camera_origin = camera.origin() + displacement;
        scene_->setCamera(Camera(new_camera_origin, camera.lookAt(), camera.up(), camera.projection_plane()));
        updateCamera();
    }
}

void MyGlWidget::updateCamera()
{
    const Camera camera = scene_->camera();
    const GlFrame camera_frame = camera.frame();
    const Point camera_origin = camera_frame.source();
    const Vector camera_up = camera.up();
    const Point camera_look = camera.lookAt();

    gluLookAt(camera_origin(0), camera_origin(1), camera_origin(2),
              camera_look(0), camera_look(1), camera_look(2),
              camera_up(0), camera_up(1), camera_up(2));
}
